Skip to content

feat(interactions): Codex read-only Feed telemetry, worktree identity, daemon health tolerance - #113

Merged
TraderSamwise merged 6 commits into
masterfrom
feat/agent-interaction-feed
Jun 10, 2026
Merged

feat(interactions): Codex read-only Feed telemetry, worktree identity, daemon health tolerance#113
TraderSamwise merged 6 commits into
masterfrom
feat/agent-interaction-feed

Conversation

@TraderSamwise

@TraderSamwise TraderSamwise commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Follow-ups to the agent-interaction bridge (#112), driven by live cmux Feed testing.

What's in here

  • fix(daemon): tolerate transient project-service health misses — the daemon hard-restarted a project service on a single 1000ms health-ping miss. Under load a busy event loop misses one ping, so healthy services were killed in a restart loop (dropping SSE streams + breaking in-flight interaction hooks). Now: 2500ms timeout + restart only after 3 consecutive failures (counter resets on success).
  • fix(codex): make permission hook telemetry-only; keep native TUI primary — Codex (unlike Claude) suppresses its native prompt while a blocking hook waits, so the only way to decide was a remote Feed card. Mirror cmux's codex behavior: the hook emits attention/telemetry and returns {}, deferring to the native TUI.
  • feat(interactions): carry agent worktree cwd in permission payload — so clients can show which project/worktree a prompt is from.
  • feat(codex): surface permission prompts as read-only Feed telemetry — non-blocking /agents/interaction/notify endpoint emits a telemetry: true interaction alert (toolName/toolInputJSON + worktree) without registering a blocking interaction; the Codex hook posts to it, then defers to the native prompt.
  • test(daemon): update health-check tests for the consecutive-failure threshold.

Verification

  • yarn verify (full suite) green via pre-push.
  • All four changes validated against a live cmux build (Claude actionable cards, Codex native-TUI-primary + read-only telemetry rows, daemon no longer restart-looping under load).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Project services now use a dedicated health-ping timeout and require consecutive failures before restarting.
    • Non-blocking telemetry notification endpoint for permission interactions; permission requests include working directory context.
  • Improvements

    • Interaction alerts now include telemetry flag, tool name, and tool input details.
    • Concurrent health checks are deduplicated to avoid duplicate restarts.
  • Tests

    • Updated supervision tests to exercise repeated/intermittent health failures and threshold behavior.

test and others added 5 commits June 9, 2026 22:15
The daemon health-checked each project service with a 1000ms timeout and
hard-restarted it (SIGTERM/respawn) on a single failure. Under load a busy
event loop can miss one ping, so healthy services were being killed in a
restart loop — dropping SSE streams and breaking in-flight interaction hooks
(observed as repeated 'health check failed: request timed out after 1000ms'
followed by terminate/respawn).

Raise the timeout to 2500ms and only restart after 3 consecutive failures
(counter resets on any success), so a transient stall no longer churns the
service.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codex's PermissionRequest hook used the same blocking long-poll as Claude
(resolvePermissionRequestOutput, 115s). Unlike Claude — which still renders its
native approval prompt while the hook blocks — Codex suppresses its native
prompt and sits at 'Working / Running PermissionRequest hook' until the hook
returns, so the only way to decide was a remote Feed card. That makes the TUI
non-primary, which is unacceptable.

Mirror cmux's codex behavior (PermissionRequest = telemetry): the codex hook now
emits a needs_input event + sets attention (so the dashboard/Feed still shows
codex needs you) and returns {} immediately, deferring to codex's native prompt.
Claude's blocking/actionable path is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
So clients can show which project/worktree a permission prompt is from, include
the hook's working dir (the worktree, or project root if none) in the
interaction payload. cmux renders it as project/worktree in the Feed card header.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codex's native TUI owns the decision, but the prompt was invisible in clients.
Add a non-blocking /agents/interaction/notify endpoint that emits a telemetry
interaction alert (interaction.telemetry=true, with toolName/toolInputJSON and
the worktree cwd) and flags attention, without registering a blocking
interaction. The codex permission hook posts to it, then defers to the native
prompt. cmux renders telemetry alerts as a non-actionable read-only row.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…hreshold

The health-tolerance fix replaces a service only after 3 consecutive failed
health checks (not the first). Update the two replacement tests to tolerate
two misses and assert replacement on the threshold-crossing third.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
app Ready Ready Preview, Comment Jun 10, 2026 5:31am

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5084bc74-2880-4ad1-a1c5-f68058db8649

📥 Commits

Reviewing files that changed from the base of the PR and between 2a8ed2d and 4fea99c.

📒 Files selected for processing (3)
  • src/daemon.ts
  • src/main.ts
  • src/metadata-server.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/metadata-server.ts
  • src/daemon.ts
  • src/main.ts

📝 Walkthrough

Walkthrough

This PR makes daemon supervision tolerate transient project-service /health failures by counting consecutive misses and only replacing services after a threshold, and changes permission-request handling to emit non-blocking telemetry notifications with enriched metadata.

Changes

Health-Check Failure Threshold Resilience

Layer / File(s) Summary
Health-check timeout and failure threshold configuration
src/daemon.ts
Introduced PROJECT_SERVICE_HEALTH_TIMEOUT_MS and a consecutive-failure threshold to tune project service health pings.
Failure counter tracking and health-check logic
src/daemon.ts
Added AimuxDaemon.projectHealthFailures map, switched health ping to use the new timeout, incremented per-project consecutive failure counts on errors, refresh existing service while below threshold, trigger replacement after threshold, and clear/reset counts on success/spawn/stop.
Health-check failure threshold tests
src/daemon.test.ts
Updated tests to simulate repeated health-check misses below threshold (no restart), threshold-crossing replacement behavior that waits for exit, and deduplication of concurrent ensures producing a single replacement spawn.

Permission Request Telemetry and Interaction Notifications

Layer / File(s) Summary
Expanded interaction alert types
src/project-events.ts
AlertEvent.interaction now carries optional telemetry, toolName, and toolInputJSON fields for telemetry-enriched alerts.
Metadata server telemetry endpoint and alert enrichment
src/metadata-server.ts
Added POST /agents/interaction/notify that sets session attention, emits a telemetry-marked interaction_request alert with toolInputJSON and worktreePath from payload.cwd, and returns { ok: true, telemetry: true } without registering a blocking interaction.
Permission request telemetry integration
src/main.ts
resolvePermissionRequestOutput accepts an optional cwd and forwards it to /agents/interaction/request; the codex-hook now posts a best-effort /agents/interaction/notify telemetry notification (toolName, input, cwd) instead of returning a resolved permission output early.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Poem

🐰 I counted failures like raindrops on leaves,
Held the service steady through jittery heaves,
Now permission whispers travel light, not shout—
Telemetry tucks worries gently out,
Hop on, systems, resilience achieves.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly covers the main changes: daemon health tolerance, codex telemetry handling, and worktree identity in interactions. It is specific and accurately reflects the primary objectives of the pull request.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/agent-interaction-feed

Comment @coderabbitai help to get the list of available commands and usage tips.

@TraderSamwise

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/daemon.ts`:
- Around line 594-596: projectHealthFailures can carry over stale failure counts
to a new PID; reset it whenever a service instance is replaced. In the spawn and
reap code paths that create or replace a project service (e.g., the
non-threshold spawn handler and the service reaper / reapService method), clear
the counter by calling this.projectHealthFailures.delete(projectId) or
this.projectHealthFailures.set(projectId, 0) right after you create or register
the new instance so the new PID starts with a fresh failure state.

In `@src/main.ts`:
- Around line 3559-3571: The telemetry call in the "permission-request" case
currently awaits postLiveProjectServiceJsonOrLocal and can throw, blocking
fallback output; make this a best-effort fire-and-forget (or catch-and-ignore)
so the hook always falls back to {}: do not let exceptions propagate from
postLiveProjectServiceJsonOrLocal called after summarizeClaudePermissionRequest
— either remove the await and let it run asynchronously or wrap the await in a
try/catch that logs/debug-ignores errors and returns immediately; ensure
sessionId, summary and payload still get passed but any thrown error from
postLiveProjectServiceJsonOrLocal does not affect the surrounding
"permission-request" case behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 60828c17-eccd-4e37-8b3f-e926714d9053

📥 Commits

Reviewing files that changed from the base of the PR and between b22ac97 and 2a8ed2d.

📒 Files selected for processing (5)
  • src/daemon.test.ts
  • src/daemon.ts
  • src/main.ts
  • src/metadata-server.ts
  • src/project-events.ts

Comment thread src/daemon.ts
Comment thread src/main.ts
- daemon: reset projectHealthFailures in spawnProjectService so a fresh service
  instance never inherits the previous pid's failure debt (covers all spawn/
  replace paths); also clear on stopProject. Keep the debt if termination fails.
- codex hook: make the /notify telemetry post best-effort (.catch) so a
  transport failure never breaks the hook — it always defers to the native prompt.
- /notify endpoint: tolerate malformed JSON (readJson catch) and reject/ignore a
  non-object payload.input, matching the /request handler's validation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@TraderSamwise

Copy link
Copy Markdown
Owner Author

Additional hardening from a parallel review pass (folded into 4fea99c):

  • /notify input validationpayload.input is now rejected/ignored when it isn't a plain object, and a malformed JSON body returns 400 instead of 500, matching the /request handler.

Intentionally not changed:

  • app/lib/events.ts AlertEvent doesn't mirror the new interaction.telemetry/toolName/toolInputJSON fields — the Expo bundle's redeclaration only carries what the app renders, and the app doesn't render interaction telemetry rows (that's the cmux Swift client). Can add later if the app gains that surface.

@TraderSamwise
TraderSamwise merged commit 18b5b90 into master Jun 10, 2026
3 checks passed
@TraderSamwise
TraderSamwise deleted the feat/agent-interaction-feed branch June 10, 2026 05:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant